package enrichment

import (
	"context"
	"fmt"

	clusterDataStore "github.com/stackrox/rox/central/cluster/datastore"
	"github.com/stackrox/rox/central/cve/fetcher"
	nodeCVEDataStore "github.com/stackrox/rox/central/cve/node/datastore"
	delegatedRegistryConfigDS "github.com/stackrox/rox/central/delegatedregistryconfig/datastore"
	"github.com/stackrox/rox/central/delegatedregistryconfig/delegator"
	"github.com/stackrox/rox/central/delegatedregistryconfig/scanwaiter"
	"github.com/stackrox/rox/central/delegatedregistryconfig/scanwaiterv2"
	"github.com/stackrox/rox/central/image/datastore"
	"github.com/stackrox/rox/central/imageintegration"
	imageIntegrationDS "github.com/stackrox/rox/central/imageintegration/datastore"
	imageV2DataStore "github.com/stackrox/rox/central/imagev2/datastore"
	"github.com/stackrox/rox/central/integrationhealth/reporter"
	namespaceDataStore "github.com/stackrox/rox/central/namespace/datastore"
	"github.com/stackrox/rox/central/role/sachelper"
	"github.com/stackrox/rox/central/sensor/service/connection"
	signatureIntegrationDataStore "github.com/stackrox/rox/central/signatureintegration/datastore"
	"github.com/stackrox/rox/central/vulnmgmt/vulnerabilityrequest/suppressor"
	v1 "github.com/stackrox/rox/generated/api/v1"
	"github.com/stackrox/rox/generated/storage"
	"github.com/stackrox/rox/pkg/env"
	"github.com/stackrox/rox/pkg/features"
	"github.com/stackrox/rox/pkg/images/cache"
	imageEnricher "github.com/stackrox/rox/pkg/images/enricher"
	"github.com/stackrox/rox/pkg/metrics"
	pkgNodeEnricher "github.com/stackrox/rox/pkg/nodes/enricher"
	"github.com/stackrox/rox/pkg/openshift"
	"github.com/stackrox/rox/pkg/sac"
	"github.com/stackrox/rox/pkg/sync"
	"github.com/stackrox/rox/pkg/utils"
)

var (
	once sync.Once

	imgEnricher            imageEnricher.ImageEnricher
	imgEnricherV2          imageEnricher.ImageEnricherV2
	nodeEnricher           pkgNodeEnricher.NodeEnricher
	depEnricher            Enricher
	orchestratorCVEManager fetcher.OrchestratorIstioCVEManager
	manager                Manager
	imageIntegrationStore  imageIntegrationDS.DataStore
)

func initialize() {
	scanDelegator := delegator.New(
		delegatedRegistryConfigDS.Singleton(),
		connection.ManagerSingleton(),
		scanwaiter.Singleton(),
		scanwaiterv2.Singleton(),

		sachelper.NewClusterNamespaceSacHelper(clusterDataStore.Singleton(), namespaceDataStore.Singleton()),
	)

	if !features.FlattenImageData.Enabled() {
		imgEnricher = imageEnricher.New(suppressor.Singleton(), imageintegration.Set(),
			metrics.CentralSubsystem, cache.ImageMetadataCacheSingleton(), datastore.Singleton().GetImage, reporter.Singleton(),
			signatureIntegrationDataStore.Singleton().GetAllSignatureIntegrations, scanDelegator)
	} else {
		imgEnricherV2 = imageEnricher.NewV2(suppressor.Singleton(), imageintegration.Set(),
			metrics.CentralSubsystem, cache.ImageMetadataCacheSingleton(), imageV2DataStore.Singleton().GetImage, reporter.Singleton(),
			signatureIntegrationDataStore.Singleton().GetAllSignatureIntegrations, scanDelegator)
	}

	nodeEnricher = pkgNodeEnricher.New(nodeCVEDataStore.Singleton(), metrics.CentralSubsystem)
	depEnricher = New(datastore.Singleton(), imageV2DataStore.Singleton(), imgEnricher, imgEnricherV2)
	orchestratorCVEManager = fetcher.SingletonManager()
	initializeManager()
}

func initializeManager() {
	ctx := sac.WithAllAccess(context.Background())
	manager = newManager(imageintegration.Set(), nodeEnricher, orchestratorCVEManager)

	imageIntegrationStore = imageIntegrationDS.Singleton()
	integrations, err := imageIntegrationStore.GetImageIntegrations(ctx, &v1.GetImageIntegrationsRequest{})
	log.Debugf("Found %d configured image integrations", len(integrations))
	if err != nil {
		log.Errorf("unable to use previous integrations: %s", err)
		return
	}
	for _, ii := range integrations {
		// Only upsert autogenerated integrations with a source if the relevant feature is enabled.
		if !shouldUpsert(ii) {
			src := utils.IfThenElse(ii.GetSource() == nil, "",
				fmt.Sprintf(" (ns %q, name %q)", ii.GetSource().GetNamespace(), ii.GetSource().GetImagePullSecretName()),
			)
			log.Debugf("Not upserting integration %q (id %q) into manager (autogenerated %t)%s", ii.GetName(), ii.GetId(), ii.GetAutogenerated(), src)
			continue
		}
		if err := manager.Upsert(ii); err != nil {
			log.Errorf("unable to use previous integration %s: %v", ii.GetName(), err)
		}
	}
}

// shouldUpsert return true if this integration should be upserted into the manager.
func shouldUpsert(ii *storage.ImageIntegration) bool {
	if !ii.GetAutogenerated() {
		// Manually created integrations should always be upserted.
		return true
	}

	if ii.GetSource() == nil {
		// If the integration doesn't have a source, always upsert it.
		return true
	}

	if env.AutogenerateGlobalPullSecRegistries.BooleanSetting() && openshift.GlobalPullSecretIntegration(ii) {
		// If this integration is from the OCP global pull secret and the feature is enabled, upsert it.
		return true
	}

	if features.SourcedAutogeneratedIntegrations.Enabled() {
		return true
	}

	return false
}

// Singleton provides the singleton Enricher to use.
func Singleton() Enricher {
	once.Do(initialize)
	return depEnricher
}

// ImageEnricherSingleton provides the singleton ImageEnricher to use.
func ImageEnricherSingleton() imageEnricher.ImageEnricher {
	once.Do(initialize)
	return imgEnricher
}

// ImageEnricherV2Singleton provides the singleton ImageEnricherV2 to use.
func ImageEnricherV2Singleton() imageEnricher.ImageEnricherV2 {
	once.Do(initialize)
	return imgEnricherV2
}

// NodeEnricherSingleton provides the singleton NodeEnricher to use.
func NodeEnricherSingleton() pkgNodeEnricher.NodeEnricher {
	once.Do(initialize)
	return nodeEnricher
}

// ManagerSingleton returns the multiplexing manager
func ManagerSingleton() Manager {
	once.Do(initialize)
	return manager
}
